#include "qe_log.h"
#include "qe_time.h"
#include "qe_assert.h"
#include "qe_memory.h"
#include "qe_driver.h"
#include "libfdt.h"



QELOG_DOMAIN("axi-i2c");



#define AXI_I2C_FIFO_DEPTH				16
#define AXI_I2C_RESET_MASK				0x0000000A
#define AXI_I2C_GIE_ENABLE_MASK			0x80000000


#define AXI_I2C_CR_GENERAL_CALL         QE_BIT(6)       /* Gen Call */
#define AXI_I2C_CR_REPEATED_START	    QE_BIT(5)		/* Repeated start */
#define AXI_I2C_CR_NO_ACK			    QE_BIT(4)		/* Transmit Acknowledge Enable */
#define AXI_I2C_CR_DIR_IS_TX		    QE_BIT(3)		/* Selects an AXI IIC transmit */
#define AXI_I2C_CR_MSMS					QE_BIT(2)		/* Master/Slave Mode Select */
#define AXI_I2C_CR_TXFIFO_RESET         QE_BIT(1)       /* Tx FIFO reset=1  */
#define AXI_I2C_CR_ENABLE				QE_BIT(0)		/* Enables the AXI IIC controller */

#define AXI_I2C_INTR_TX_HALF            QE_BIT(7)       /* TX FIFO half empty */
#define AXI_I2C_INTR_NAAS               QE_BIT(6)       /* not addr as slave */
#define AXI_I2C_INTR_AAS                QE_BIT(5)       /* when addr as slave */
#define AXI_I2C_INTR_BNB			    QE_BIT(4)		/* IIC Bus is Not Busy */
#define AXI_I2C_INTR_RX_FIFO_FULL	    QE_BIT(3)		/* Rx FIFO Full */
#define AXI_I2C_INTR_TX_FIFO_EMPTY	    QE_BIT(2)		/* Tx FIFO Empty */
#define AXI_I2C_INTR_TX_ERROR	        QE_BIT(1)		/* Tx Error */
#define AXI_I2C_INTR_ARB_LOST		    QE_BIT(0)		/* Arbitration Lost */

#define AXI_I2C_SR_TX_FIFO_EMPTY        QE_BIT(7)       /* Tx FIFO empty */
#define AXI_I2C_SR_RX_FIFO_EMPTY        QE_BIT(6)       /* Rx FIFO empty */
#define AXI_I2C_SR_RX_FIFO_FULL         QE_BIT(5)       /* Rx FIFO full */
#define AXI_I2C_SR_TX_FIFO_FULL			QE_BIT(4)		/* Tx FIFO full */
#define AXI_I2C_SR_MSTR_RDING_SLAVE     QE_BIT(3)       /* mstr <-- slave */
#define AXI_I2C_SR_BUS_BUSY		        QE_BIT(2)		/* Bus is busy */
#define AXI_I2C_SR_ADDR_AS_SLAVE	    QE_BIT(1)		/* When addressed as slave */
#define AXI_I2C_SR_GEN_CALL             QE_BIT(0)       /* Gen call */

#define AXI_I2C_TX_DYN_START            QE_BIT(8)       /* 1 = Set dynamic start */
#define AXI_I2C_TX_DYN_STOP             QE_BIT(9)       /* 1 = Set dynamic stop */



typedef struct 
{
	volatile qe_u32 reserve1[7];
	volatile qe_u32 gie;			/* Global Interrupt Enable Register */
	volatile qe_u32 isr;			/* Interrupt Status Register */
	volatile qe_u32 reserve2;
	volatile qe_u32 ier;			/* Interrupt Enable Register */
	volatile qe_u32 reserve3[5];
	volatile qe_u32 reset;			/* Soft Reset Register */
	volatile qe_u32 reserve4[47];
	volatile qe_u32 cr;			    /* Control Register */
	volatile qe_u32 sr;			    /* Status Register */
	volatile qe_u32 txfifo;		    /* Transmit FIFO Register */
	volatile qe_u32 rxfifo;		    /* Receive FIFO Register */
	volatile qe_u32 sar;			/* Slave Address Register */
	volatile qe_u32 tfo;			/* Transmit FIFO Occupancy Register */
	volatile qe_u32 rfo;			/* Transmit FIFO Occupancy Register */
	volatile qe_u32 stbar;			/* Slave Ten Bit Address Register */
	volatile qe_u32 rfd;			/* Receive FIFO Programmable Depth Interrupt Register */
	volatile qe_u32 gpo;			/* General Purpose Output Register */
	volatile qe_u32 tsusta;		    /* Timing Parameter Register */
	volatile qe_u32 tsusto;		    /* Timing Parameter Register */
	volatile qe_u32 thdsta;		    /* Timing Parameter Register */
	volatile qe_u32 tsudat;		    /* Timing Parameter Register */
	volatile qe_u32 tbuf;			/* Timing Parameter Register */
	volatile qe_u32 thigh;			/* Timing Parameter Register */
	volatile qe_u32 tlow;			/* Timing Parameter Register */
	volatile qe_u32 thddat;		    /* Timing Parameter Register */
} axi_i2c_reg;

typedef struct 
{
    axi_i2c_reg *reg;
    qe_intc_dev *intc;
    qe_i2c_bus bus;
    qe_uint irq;
    qe_uint errors;
    qe_bool tx_complete;
    qe_bool rx_complete;
} axi_i2c_dev;

static void axi_i2c_hw_init(axi_i2c_dev *dev)
{
    /* Software reset */
    dev->reg->reset = AXI_I2C_RESET_MASK;

    /* Disable interrupt */
    dev->reg->ier = 0;
    dev->reg->gie = 0;

	dev->rx_complete = 0;
	dev->tx_complete = 0;
}

static void clear_rx_fifo(axi_i2c_dev *dev)
{
    while (!(dev->reg->sr & AXI_I2C_SR_RX_FIFO_EMPTY));
}

static void irq_clr(axi_i2c_dev *dev, qe_u32 mask)
{
    dev->reg->isr &= mask;
}

static void axi_i2c_reinit(axi_i2c_dev *dev)
{
    dev->reg->reset = AXI_I2C_RESET_MASK;

	/* Set receive Fifo depth to maximum (zero based). */
    dev->reg->rfd = AXI_I2C_FIFO_DEPTH - 1;

	/* Reset Tx Fifo. */
    dev->reg->cr = AXI_I2C_CR_TXFIFO_RESET;

	/* Enable IIC Device, remove Tx Fifo reset & disable general call. */
    dev->reg->cr = AXI_I2C_CR_ENABLE;

	/* make sure RX fifo is empty */
	clear_rx_fifo(dev);

	/* Disable interrupts */
    dev->reg->gie = 0;

	irq_clr(dev, AXI_I2C_INTR_ARB_LOST);    
}

static qe_bool is_bus_busy(axi_i2c_dev *dev)
{
	if (dev->reg->sr & AXI_I2C_SR_BUS_BUSY)
		return qe_true;
	return qe_false;
}

static qe_ret wait_bus_free(axi_i2c_dev *dev, qe_uint timeout)
{
	qe_u32 count = 0;

	while (is_bus_busy(dev)) {
		qe_usleep(1);
		if (count++ > timeout) {
			return qe_err_timeout;
		}
	}

	return qe_ok;
}

static qe_bool wait_rxfifo_not_empty(axi_i2c_dev *dev, qe_uint timeout)
{
    qe_u32 count = 0;

    while (dev->reg->sr & AXI_I2C_SR_RX_FIFO_EMPTY) {
        qe_usleep(100000);
		if (count++ > timeout) {
			return qe_false;
		}
    }

    return qe_true;
}

static qe_bool wait_txfifo_empty(axi_i2c_dev *dev, qe_uint timeout)
{
    qe_u32 count = 0;

    while (!(dev->reg->sr & AXI_I2C_SR_TX_FIFO_EMPTY)) {
        qe_usleep(1);
		if (count++ > timeout) {
			return qe_false;
		}
    }

    return qe_true;
}

static qe_u8 i2c_8bit_addr_from_flags(qe_uint addr, qe_u16 flags)
{
	return (addr << 1) | (flags & QE_I2C_RD ? 1 : 0);
}

static void set_address(axi_i2c_dev *dev, qe_u8 addr, qe_u16 flags, qe_uint len, qe_uint nmsgs)
{
	irq_clr(dev, AXI_I2C_INTR_TX_ERROR);

	if (!(flags & QE_I2C_NOSTART)) {
		/* write the address */
		qe_u16 data = i2c_8bit_addr_from_flags(addr, flags) |
			AXI_I2C_TX_DYN_START;
		if (nmsgs == 1 && len == 0)
			/* no data and last message -> add STOP */
			data |= AXI_I2C_TX_DYN_STOP;

        dev->reg->txfifo = data;
	}
}

static qe_ret axi_i2c_read_rx(axi_i2c_dev *dev, qe_i2c_message *msg, qe_uint nmsgs)
{
    int i;
    qe_u32 pos = 0;
    qe_ret ret;
	qe_u8 bytes_in_fifo;

	while (pos < msg->len) {

        if (!wait_rxfifo_not_empty(dev, 20000)) {
            qe_error("wait rxfifo not empty timeout");
            return qe_err_timeout;
        }

        bytes_in_fifo = dev->reg->rfd + 1;

		if (bytes_in_fifo > msg->len)
			bytes_in_fifo = msg->len;

		for (i = 0; i < bytes_in_fifo; i++) {
			msg->buf[pos++] = dev->reg->rxfifo;
		}
	}

	return qe_ok;
}

static int tx_fifo_space(axi_i2c_dev *dev)
{
	/* return the actual space left in the FIFO */
	return AXI_I2C_FIFO_DEPTH - dev->reg->tfo - 1;
}

static void fill_tx_fifo(axi_i2c_dev *dev, qe_i2c_message *msg, qe_uint nmsgs)
{
	qe_u8 fifo_space = tx_fifo_space(dev);
	int len = msg->len;
	qe_u32 pos = 0;

	len = (len > fifo_space) ? fifo_space : len;

	while (len--) {
		qe_u16 data = msg->buf[pos++];

		if ((msg->len - pos == 0) && nmsgs == 1) {
			/* last message in transfer -> STOP */
			data |= AXI_I2C_TX_DYN_STOP;
		}
        dev->reg->txfifo = data;
	}
}

static qe_ret axi_i2c_read(axi_i2c_dev *dev, qe_i2c_message *msg, qe_uint nmsgs)
{
    qe_u8 rx_watermark;

	/* Clear and enable Rx full interrupt. */
	irq_clr(dev, AXI_I2C_INTR_RX_FIFO_FULL | AXI_I2C_INTR_TX_ERROR);

	/* we want to get all but last byte, because the TX_ERROR IRQ is used
	 * to inidicate error ACK on the address, and negative ack on the last
	 * received byte, so to not mix them receive all but last.
	 * In the case where there is only one byte to receive
	 * we can check if ERROR and RX full is set at the same time
	 */
    rx_watermark = msg->len;
	if (rx_watermark > AXI_I2C_FIFO_DEPTH)
		rx_watermark = AXI_I2C_FIFO_DEPTH;
    
    dev->reg->rfd = rx_watermark - 1;
    
    set_address(dev, msg->addr, msg->flags, msg->len, nmsgs);

    irq_clr(dev, AXI_I2C_INTR_BNB);

    dev->reg->txfifo = (msg->len & 0xff) | ((nmsgs == 1) ? AXI_I2C_TX_DYN_STOP : 0);

    if (nmsgs == 1)
        irq_clr(dev, AXI_I2C_INTR_BNB);
    
    return axi_i2c_read_rx(dev, msg, nmsgs);
}

static qe_ret axi_i2c_write(axi_i2c_dev *dev, qe_i2c_message *msg, qe_uint nmsgs)
{
	set_address(dev, msg->addr, msg->flags, msg->len, nmsgs);
	fill_tx_fifo(dev, msg, nmsgs);

    if (!(wait_txfifo_empty(dev, 20000))) {
        qe_error("wait txfifo empty timeout");
        return qe_err_timeout;
    }

	/* Clear any pending Tx empty, Tx Error and then enable them. */
	irq_clr(dev, AXI_I2C_INTR_TX_FIFO_EMPTY | AXI_I2C_INTR_TX_ERROR |
			   AXI_I2C_INTR_BNB);

	return qe_ok;
}

static qe_int axi_i2c_xfer(qe_i2c_bus *bus, qe_i2c_message *msgs, qe_uint num)
{
    int i = 0;
	qe_ret ret;
    axi_i2c_dev *dev = (axi_i2c_dev *)bus->priv;

    qe_debug("xfer %d msgs", num);

	ret = wait_bus_free(dev, 20000);
    if (ret != qe_ok) {
        qe_error("timeout wait bus free");
        return -(ret);
    }

    qe_debug("do reinit");
    axi_i2c_reinit(dev);

    for (; num>0; num--, msgs++) {
        
        if (msgs->flags & QE_I2C_RD) {
            ret = axi_i2c_read(dev, msgs, num);
        } else {
            ret = axi_i2c_write(dev, msgs, num);
        }

        if (ret != qe_ok) {
            qe_error("xfer err:%d", ret);
            return -ret;
        }

        i++;
    }

    return i;
} 

static qe_i2c_bus_ops axi_i2c_ops = {
    .master_xfer = axi_i2c_xfer
};

static qe_ret axi_i2c_probe(const void *fdt, int offset)
{
    int len;
    const char *name;
    const char *name_intc;
    const qe_u32 *prop_reg;
    const qe_u32 *prop_irq;
    qe_intc_dev *intc;
    axi_i2c_dev *dev;

    name = fdt_get_name(fdt, offset, QE_NULL);

    prop_reg = (const qe_u32 *)fdt_getprop(fdt, offset, "reg", &len);
    if (!prop_reg) {
        qe_error("%s no reg prop", name);
        return qe_err_param;
    }

    name_intc = qe_of_get_interrupt_parent_name(fdt, offset);
    if (name_intc) {
        intc = (qe_intc_dev *)qe_dev_find(name_intc);
        if (!intc) {
            qe_error("%s not found", name_intc);
            return qe_err_notfind;
        }
        prop_irq = (const qe_u32 *)fdt_getprop(fdt, offset, "interrupts", &len);
        if (!prop_irq) {
            qe_error("%s no interrupts prop", name);
            return qe_err_param;
        }
    }

    dev = qe_malloc(sizeof(axi_i2c_dev));
    qe_assert(dev);

    dev->errors = 0;

    dev->reg = (axi_i2c_reg *)(qe_ubase)fdt32_to_cpu(*prop_reg);

    axi_i2c_hw_init(dev);

    qe_i2c_bus_register(&dev->bus, name, &axi_i2c_ops, dev);

    qe_debug("register %s %x", name, dev);

    return qe_ok;
}

static qe_device_id axi_i2c_ids[] = {
    {.compatible = "xlnx,axi-i2c"},
    {},
};

QE_DRIVER(axi_i2c_v2) = {
    .name = "axi-i2c-v2",
    .of_match = axi_i2c_ids,
    .probe = axi_i2c_probe,
};

QE_DRIVER_FORCE_EXPORT(axi_i2c_v2);